/*
@Time : 2022/7/14 19:49
@Author : gaozhichang
@File : signal.go
@des: 利用信号，做热启动，热加载
*/
package main

import (
	"fmt"
	"os"
	"os/signal"
	"runtime"
	"runtime/pprof"
	"runtime/trace"
	"syscall"
	"time"
)

/**
在 linux 系统，可以通过kill -signal_number pid命令向程序发送指定信号。
如上代码所示，我们硬编码指定的采样开关信号值是 31。因此，当程序运行起来后，我们在控制台输入kill -31 pid 命令，即可开启采样，再次输入kill -31 pid命令，就关闭了采样。
 */
func main()   {
	RegisterSignalForPrintStack(syscall.Signal(31))
	//RegisterSignalForProfiling(syscall.Signal(31))

	t := time.Tick(2 * time.Second)
	for {
		select {
		case <-t:
			fmt.Println("sleep 2 seconds...")
		}
	}
}

//依葫芦画瓢，我们再来一个打印 goroutine 堆栈信息的热开关函数，是不是很酷？
func RegisterSignalForPrintStack(sig os.Signal) {
	ch := make(chan os.Signal)
	signal.Notify(ch, sig)

	go func() {
		for range ch {
			buffer := make([]byte, 1024*1024*4)
			runtime.Stack(buffer, true)
			fmt.Println("in RegisterSignalForPrintStack")
			fmt.Println(string(buffer))
		}
	}()
}

/**
我们可以将基于接口触发的方式改为信号通知。
首先，构造采样功能函数（对应于 net/http/pprof 包下 init 函数中绑定的路由功能函数）。

在上述函数中，我们定义了接收信号通道ch，通过signal.Notify(ch, sig)将指定的通知信号sig与ch进行绑定。for range ch 将阻塞等待外部信号sig，随着sig信号的到来，交替进入开启或关闭采样的逻辑。
 */
func RegisterSignalForProfiling(sig os.Signal) {
	ch := make(chan os.Signal)
	started := false
	signal.Notify(ch, sig)

	go func() {
		var memoryProfile, cpuProfile, traceProfile *os.File
		for range ch {
			if started {
				pprof.StopCPUProfile()
				trace.Stop()
				pprof.WriteHeapProfile(memoryProfile)
				memoryProfile.Close()
				cpuProfile.Close()
				traceProfile.Close()
				started = false
			} else {
				cpuProfile, _ = os.Create("cpu.pprof")
				memoryProfile, _ = os.Create("memory.pprof")
				traceProfile, _ = os.Create("runtime.trace")
				pprof.StartCPUProfile(cpuProfile)
				trace.Start(traceProfile)
				started = true
			}
		}
	}()
}
